/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                        Intel License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of Intel Corporation may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

/*
 * performance.cpp
 *
 * Measure performance of classifier
 */
#include <cv.h>
#include <highgui.h>

#include <cstdio>
#include <cmath>
#include <ctime>

#ifdef _WIN32
/* use clock() function insted of time() */
#define time( arg ) (((double) clock()) / CLOCKS_PER_SEC)
#endif /* _WIN32 */

#ifndef PATH_MAX
#define PATH_MAX 512
#endif /* PATH_MAX */

typedef struct HidCascade {
    int size;
    int count;
} HidCascade;

typedef struct ObjectPos {
    float x;
    float y;
    float width;
    int found;    /* for reference */
    int neghbors;
} ObjectPos;

int main(int argc, char* argv[]) {
    int i, j;
    char* classifierdir = NULL;
    //char* samplesdir    = NULL;

    int saveDetected = 1;
    double scale_factor = 1.2;
    float maxSizeDiff = 1.5F;
    float maxPosDiff  = 0.3F;

    /* number of stages. if <=0 all stages are used */
    int nos = -1, nos0;

    int width  = 24;
    int height = 24;

    int rocsize;

    FILE* info;
    char* infoname;
    char fullname[PATH_MAX];
    char detfilename[PATH_MAX];
    char* filename;
    char detname[] = "det-";

    CvHaarClassifierCascade* cascade;
    CvMemStorage* storage;
    CvSeq* objects;

    double totaltime;

    infoname = (char*)"";
    rocsize = 40;
    if (argc == 1) {
        printf("Usage: %s\n  -data <classifier_directory_name>\n"
               "  -info <collection_file_name>\n"
               "  [-maxSizeDiff <max_size_difference = %f>]\n"
               "  [-maxPosDiff <max_position_difference = %f>]\n"
               "  [-sf <scale_factor = %f>]\n"
               "  [-ni]\n"
               "  [-nos <number_of_stages = %d>]\n"
               "  [-rs <roc_size = %d>]\n"
               "  [-w <sample_width = %d>]\n"
               "  [-h <sample_height = %d>]\n",
               argv[0], maxSizeDiff, maxPosDiff, scale_factor, nos, rocsize,
               width, height);

        return 0;
    }

    for (i = 1; i < argc; i++) {
        if (!strcmp(argv[i], "-data")) {
            classifierdir = argv[++i];
        } else if (!strcmp(argv[i], "-info")) {
            infoname = argv[++i];
        } else if (!strcmp(argv[i], "-maxSizeDiff")) {
            maxSizeDiff = (float) atof(argv[++i]);
        } else if (!strcmp(argv[i], "-maxPosDiff")) {
            maxPosDiff = (float) atof(argv[++i]);
        } else if (!strcmp(argv[i], "-sf")) {
            scale_factor = atof(argv[++i]);
        } else if (!strcmp(argv[i], "-ni")) {
            saveDetected = 0;
        } else if (!strcmp(argv[i], "-nos")) {
            nos = atoi(argv[++i]);
        } else if (!strcmp(argv[i], "-rs")) {
            rocsize = atoi(argv[++i]);
        } else if (!strcmp(argv[i], "-w")) {
            width = atoi(argv[++i]);
        } else if (!strcmp(argv[i], "-h")) {
            height = atoi(argv[++i]);
        }
    }

    cascade = cvLoadHaarClassifierCascade(classifierdir, cvSize(width, height));
    if (cascade == NULL) {
        printf("Unable to load classifier from %s\n", classifierdir);

        return 1;
    }

    int* numclassifiers = new int[cascade->count];
    numclassifiers[0] = cascade->stage_classifier[0].count;
    for (i = 1; i < cascade->count; i++) {
        numclassifiers[i] = numclassifiers[i - 1] + cascade->stage_classifier[i].count;
    }

    storage = cvCreateMemStorage();

    nos0 = cascade->count;
    if (nos <= 0) {
        nos = nos0;
    }

    strcpy(fullname, infoname);
    filename = strrchr(fullname, '\\');
    if (filename == NULL) {
        filename = strrchr(fullname, '/');
    }
    if (filename == NULL) {
        filename = fullname;
    } else {
        filename++;
    }

    info = fopen(infoname, "r");
    totaltime = 0.0;
    if (info != NULL) {
        int x, y, width, height;
        IplImage* img;
        int hits, missed, falseAlarms;
        int totalHits, totalMissed, totalFalseAlarms;
        int found;
        float distance;

        int refcount;
        ObjectPos* ref;
        int detcount;
        ObjectPos* det;
        int error = 0;

        int* pos;
        int* neg;

        pos = (int*) cvAlloc(rocsize * sizeof(*pos));
        neg = (int*) cvAlloc(rocsize * sizeof(*neg));
        for (i = 0; i < rocsize; i++) { pos[i] = neg[i] = 0; }

        printf("+================================+======+======+======+\n");
        printf("|            File Name           | Hits |Missed| False|\n");
        printf("+================================+======+======+======+\n");

        totalHits = totalMissed = totalFalseAlarms = 0;
        while (!feof(info)) {
            if (fscanf(info, "%s %d", filename, &refcount) != 2 || refcount <= 0) { break; }

            img = cvLoadImage(fullname);
            if (!img) { continue; }

            ref = (ObjectPos*) cvAlloc(refcount * sizeof(*ref));
            for (i = 0; i < refcount; i++) {
                error = (fscanf(info, "%d %d %d %d", &x, &y, &width, &height) != 4);
                if (error) { break; }
                ref[i].x = 0.5F * width  + x;
                ref[i].y = 0.5F * height + y;
                ref[i].width = sqrtf(0.5F * (width * width + height * height));
                ref[i].found = 0;
                ref[i].neghbors = 0;
            }
            if (!error) {
                cvClearMemStorage(storage);

                cascade->count = nos;
                totaltime -= time(0);
                objects = cvHaarDetectObjects(img, cascade, storage, scale_factor, 1);
                totaltime += time(0);
                cascade->count = nos0;

                detcount = (objects ? objects->total : 0);
                det = (detcount > 0) ?
                      ((ObjectPos*)cvAlloc(detcount * sizeof(*det))) : NULL;
                hits = missed = falseAlarms = 0;
                for (i = 0; i < detcount; i++) {
                    CvAvgComp r = *((CvAvgComp*) cvGetSeqElem(objects, i));
                    det[i].x = 0.5F * r.rect.width  + r.rect.x;
                    det[i].y = 0.5F * r.rect.height + r.rect.y;
                    det[i].width = sqrtf(0.5F * (r.rect.width * r.rect.width +
                                                 r.rect.height * r.rect.height));
                    det[i].neghbors = r.neighbors;

                    if (saveDetected) {
                        cvRectangle(img, cvPoint(r.rect.x, r.rect.y),
                                    cvPoint(r.rect.x + r.rect.width, r.rect.y + r.rect.height),
                                    CV_RGB(255, 0, 0), 3);
                    }

                    found = 0;
                    for (j = 0; j < refcount; j++) {
                        distance = sqrtf((det[i].x - ref[j].x) * (det[i].x - ref[j].x) +
                                         (det[i].y - ref[j].y) * (det[i].y - ref[j].y));
                        if ((distance < ref[j].width * maxPosDiff) &&
                                (det[i].width > ref[j].width / maxSizeDiff) &&
                                (det[i].width < ref[j].width * maxSizeDiff)) {
                            ref[j].found = 1;
                            ref[j].neghbors = MAX(ref[j].neghbors, det[i].neghbors);
                            found = 1;
                        }
                    }
                    if (!found) {
                        falseAlarms++;
                        neg[MIN(det[i].neghbors, rocsize - 1)]++;
                    }
                }
                for (j = 0; j < refcount; j++) {
                    if (ref[j].found) {
                        hits++;
                        pos[MIN(ref[j].neghbors, rocsize - 1)]++;
                    } else {
                        missed++;
                    }
                }

                totalHits += hits;
                totalMissed += missed;
                totalFalseAlarms += falseAlarms;
                printf("|%32.32s|%6d|%6d|%6d|\n", filename, hits, missed, falseAlarms);
                printf("+--------------------------------+------+------+------+\n");
                fflush(stdout);

                if (saveDetected) {
                    strcpy(detfilename, detname);
                    strcat(detfilename, filename);
                    strcpy(filename, detfilename);
                    cvvSaveImage(fullname, img);
                }

                if (det) { cvFree(&det); det = NULL; }
            } /* if( !error ) */

            cvReleaseImage(&img);
            cvFree(&ref);
        }
        fclose(info);

        printf("|%32.32s|%6d|%6d|%6d|\n", "Total",
               totalHits, totalMissed, totalFalseAlarms);
        printf("+================================+======+======+======+\n");
        printf("Number of stages: %d\n", nos);
        printf("Number of weak classifiers: %d\n", numclassifiers[nos - 1]);
        printf("Total time: %f\n", totaltime);

        /* print ROC to stdout */
        for (i = rocsize - 1; i > 0; i--) {
            pos[i - 1] += pos[i];
            neg[i - 1] += neg[i];
        }
        fprintf(stderr, "%d\n", nos);
        for (i = 0; i < rocsize; i++) {
            fprintf(stderr, "\t%d\t%d\t%f\t%f\n", pos[i], neg[i],
                    ((float)pos[i]) / (totalHits + totalMissed),
                    ((float)neg[i]) / (totalHits + totalMissed));
        }

        cvFree(&pos);
        cvFree(&neg);
    }

    delete[] numclassifiers;

    cvReleaseHaarClassifierCascade(&cascade);
    cvReleaseMemStorage(&storage);

    return 0;
}

